// Copyright (c) 2023 CHANGLEI. All rights reserved.

part of 'arith.dart';

/// Created by changlei on 2023/6/7.
///
/// 变量
final class Variable1 with Describable implements Argument {
  /// 变量
  const Variable1(this.symbol);

  /// 值
  final String symbol;

  /// 转为[Number1]
  num? tryParse(Map<String, ValueGetter<num>>? variables) {
    return (variables?[symbol] ?? variables?['`$symbol`'])?.call() ?? num.tryParse(symbol);
  }

  /// 转为[Number1]
  num parse(Map<String, ValueGetter<num>>? variables) {
    final value = tryParse(variables);
    if (value == null) {
      throw ArithError.variable(symbol);
    }
    return value;
  }

  @override
  String toDescribe([bool isTight = false, Map<String, ValueGetter<num>>? variables]) {
    return (variables?[symbol]?.call() ?? symbol).toString();
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is Variable1 && runtimeType == other.runtimeType && symbol == other.symbol;

  @override
  int get hashCode => symbol.hashCode;

  /// 解析
  static Variables of(String s) => Variables([Variable1(s)]);
}

/// 变量，两个参数
final class Variables with Iterable<Argument>, Describable implements Operable {
  /// 变量
  const Variables(this._arguments);

  /// 值
  final Iterable<Argument> _arguments;

  /// 转为[Numbers]
  List<num> parse(
    Map<String, ValueGetter<num>>? variables, [
    void Function(List<Argument> before, Argument argument, ArithError error)? onError,
  ]) {
    final numbers = <num>[];
    final before = <Argument>[];
    for (final argument in this) {
      try {
        numbers.add(
          switch (argument) {
            Variable1() => argument.parse(variables),
            Expression() => argument(variables),
          },
        );
      } on ArithError catch (e) {
        if (onError == null) {
          rethrow;
        } else {
          onError(before, argument, e);
        }
      }
      before.add(argument);
    }
    return numbers;
  }

  @override
  Iterator<Argument> get iterator => _arguments.iterator;

  @override
  String toDescribe([bool isTight = false, Map<String, ValueGetter<num>>? variables]) {
    final factors = <String>[];
    for (final value in this) {
      factors.add(
        switch (value) {
          Variable1(:final toDescribe) => toDescribe(isTight, variables),
          Expression(:final toDescribe) => toDescribe(isTight, variables),
        },
      );
    }
    return factors.join(isTight ? ',' : ', ');
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other is! Variables || runtimeType != other.runtimeType || length != other.length) {
      return false;
    }
    for (final (index, e) in _arguments.indexed) {
      if (e != other.elementAt(index)) {
        return false;
      }
    }
    return true;
  }

  @override
  int get hashCode => Object.hash(super.hashCode, Object.hashAll(_arguments));

  /// 解析
  static Arith of(String s, Parser parser) {
    final arguments = List.of(_parse(s).map(parser.parse));
    final singleOrNull = arguments.singleOrNull;
    if (singleOrNull is Expression) {
      int depth = 0;
      for (final factor in singleOrNull) {
        if (factor == Curve.left) {
          depth++;
        } else if (factor == Curve.right) {
          depth--;
        }
      }
      if (depth == 0) {
        return singleOrNull;
      }
    }
    return Variables(arguments);
  }

  static Iterable<String> _parse(String s) sync* {
    int level = 0;
    final current = StringBuffer();
    for (int i = 0; i < s.length; i++) {
      if (s[i] == Curve.left.toString()) {
        level++;
      } else if (s[i] == Curve.right.toString()) {
        level--;
      } else if (s[i] == ',' && level == 0) {
        final string = current.toString().trim();
        if (string.isNotEmpty) {
          yield string;
        }
        current.clear();
        continue;
      }
      current.write(s[i]);
    }
    final string = current.toString().trim();
    if (string.isNotEmpty) {
      yield string;
    }
  }
}
